/* Copyright (C) 2008  Romolo Camplani, Marco Fortunato, Marco Marelli, Guido Rota, Fabio A. Schreiber et al.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.dei.perla.sys.device.channel.frame.receiver;

import org.dei.perla.sys.device.channel.frame.FrameConfiguration;

/**
 * Questa classe realizza un automa a stati per l'individuazione di frames
 * a partire da uno stream di bytes.
 * 
 * L'automa supporta due tipi di frames:
 * i) Frame privo di header
 * +--------------------------+-------------------+-------------+
 * | Delim. inizio headerless | Payload (escaped) | Terminatore |
 * +--------------------------+-------------------+-------------+
 * ii) Frame con header generico
 * +--------------------------+-----------------+------------------+-------------+
 * | Delim. inizio headerful  | Header generico |Payload (escaped) | Terminatore |
 * +--------------------------+-----------------+------------------+-------------+
 */
public abstract class FrameReceiver {

	/**
	 * Parametri di configurazione
	 */
	protected FrameConfiguration pConfiguration;

	/**
	 * Gestore eventi di ricezione
	 */
	protected FrameReceiverEvents pEvents;

	/**
	 * Buffer di ricezione
	 */
	protected byte[] pBuffer;

	/**
	 * Numero di byte validi presenti nel buffer di ricezione
	 */
	protected int pBufferCount;

	/**
	 * Costruttore
	 * @param parConfiguration Parametri di configurazione del ricevitore
	 */
	public FrameReceiver(FrameConfiguration parConfiguration) {

		// Memorizza l'oggetto con i parametri di configurazione
		this.pConfiguration = parConfiguration;

		// Istanzia il buffer locale
		this.pBuffer = new byte[this.pConfiguration.getBufferSize()];

		// Resetta il buffer
		this.pBufferReset();

	}
	
	/**
	 * Inizializza il ricevitore di frame impostando un gestore eventi di ricezione
	 * @param parEvents Gestore eventi di ricezione
	 */
	public void initialize(FrameReceiverEvents parEvents) {
		this.pEvents = parEvents;
	}
	
	/**
	 * Restituisce la configurazione
	 */
	public FrameConfiguration getConfiguration() {
		return this.pConfiguration;
	}

	/**
	 * Svuota il buffer di ricezione
	 */
	protected void pBufferReset() {
		this.pBufferCount = 0;
	}

	/**
	 * Aggiunge un byte al buffer di ricezione. Restituisce false quando il buffer è pieno.
	 */
	protected boolean pBufferAppend(byte parByteToAppend) {
		if (this.pBufferCount < this.pConfiguration.getBufferSize()) {
			this.pBuffer[this.pBufferCount++] = parByteToAppend;
			return true;
		}
		this.pNotifyBufferOverflow(parByteToAppend);
		return false;
	}

	/**
	 * Invia un evento di errore, segnalando il ricevimento di un frame senza terminatore
	 */
	protected void pNotifyUnclosedFrame() {

		String tErrorMessage = "";

		// Costruisce il messaggio di errore
		tErrorMessage += "Ricevuto frame senza terminatore (";

		// Aggiunge al messaggio i byte che costituiscono il frame non terminato
		for (int iReceivedByte = 0; iReceivedByte < this.pBufferCount; ++iReceivedByte) {
			if (iReceivedByte > 0)
				tErrorMessage += ", ";
			tErrorMessage += this.pBuffer[iReceivedByte];
		}

		// Termina di costruire il messaggio
		tErrorMessage += ").";

		// Invia l'evento
		this.pEvents.onError(tErrorMessage);
	}

	/**
	 * Invia un evento di errore, segnalando il ricevimento di un byte non valido (fuori da un frame)
	 */
	protected void pNotifyInvalidByte(byte parByte) {

		String tErrorMessage = "";

		// Costruise il messaggio di errore
		tErrorMessage += "Ricevuto byte fuori sequenza (" + parByte + ")";

		// Invia l'evento
		this.pEvents.onError(tErrorMessage);
	}

	/**
	 * Invia un evento di errore, segnalando la perdita di un byte del frame (buffer pieno)
	 */
	protected void pNotifyBufferOverflow(byte parByte) {

		String tErrorMessage = "";

		// Costruise il messaggio di errore
		tErrorMessage += "Ricevuto e scartato byte (" + parByte
				+ ") a causa di overflow del buffer di ricezione";

		// Invia l'evento
		this.pEvents.onError(tErrorMessage);
	}

	/**
	 * Invia un evento di errore, segnalando la ricezione di una sequenza di escape non valida	 
	 */
	protected void pNotifyInvalidEscape(byte parByte) {

		String tErrorMessage = "";

		// Costruisce il messaggio di errore
		tErrorMessage += "Ricevuta sequenza di escape non valida (" + parByte
				+ ")";

		// Invia l'evento
		this.pEvents.onError(tErrorMessage);
	}

	/**
	 * Invia un evento per notificare la ricezione di un frame
	 */
	abstract protected void pNotifyFrame(); 

	/**
	 * Resetta lo stato del ricevitore
	 */
	abstract public void reset();

	/**
	 * Riceve il byte specificato dal parametro
	 * @param parReceivedByte Byte da elaborare
	 */
	abstract public void receiveByte(byte parReceivedByte); 
	
}
